#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>

#include "st.h"
#include "ic.h"
#include "cg.h"

/* current nesting level */
int ncur = 0;

int is_const(char *s) {
	int i;
	
	for(i=0; i<strlen(s); i++) {
		if(!isdigit(s[i])) {
			return 0;
		}
	}
	
	return 1;
}

char * label( char *l ){
    char s[35];
    sprintf(s, ".L%s", l);
    return strdup(s);
}

void print_machine_code(){
    CODE *c = mc_tail;
	FILE *out = fopen("assembly.s", "w");
	if (out == NULL) {
		fprintf(stderr, "Error: Can't open icode.txt for writing.\n");
		exit(1);
	}
    
    while(c != NULL){
		fprintf(out, "%s", c->cmd);
		c = c->prev;
	}
	
	fclose(out);
}

void add_machine_code( char *s ){
    CODE *tmp;
    
    tmp = (CODE*)malloc(sizeof(CODE));
    sprintf(tmp->cmd, "%s", s);
    
    if ( mc == NULL ){
        tmp->next = NULL;
        tmp->prev = NULL;
        mc = tmp;
        mc_tail = tmp;
    }else{
        mc->prev = tmp;
        tmp->next = mc;
        mc = tmp;
    }
}

void start_code_generation( char *prog_name){
    char s_tmp[255];
    
    sprintf(s_tmp , "\
.text\n\
.global main\n\
main:\n\
\tcall %s\n\
\tret\n\
\n", prog_name);

    add_machine_code( s_tmp );
}

void end_code_generation(){
    char s_tmp[255];	

    sprintf(s_tmp, "\
print:\n\
\t.att_syntax\n\
\tpushl %%eax\n\
\tpushl $s\n\
\t.intel_syntax\n\
\tcall printf\n\
\t.att_syntax\n\
\taddl $8, %%esp\n\
\t.intel_syntax\n\
\tret\n\
\t.att_syntax\n\
.data\n\
\ts: .asciz \"%%d\\n\"\n\
\n\
");

    add_machine_code( s_tmp );
}

void load( char *R, char *a ){
	char s_tmp[255];
    int f = stderr;
        
    if ( !isalpha(*a) ){
        sprintf(s_tmp, "\tmovl $%s, %%%s\n", a, R);
        add_machine_code( s_tmp );
    }else{
        SYMBOL *p = lookup(a);
		fprintf(stderr, "%s level=%d ncur=%d\n", p->name, p->level, ncur);
		exit(-1);
		
		/* TODO */
		
    }
}

void store( char *R, char *a ){
	
}

void getAR( SYMBOL *s ) {
	char s_tmp[255];
    int f = stderr;
    int i;
    
    sprintf(s_tmp, "\tmovl 8(%%ebp), %%esi\n");
    add_machine_code( s_tmp );
    
	fprintf(stderr, "%s\n", s->level);

/*    
if ( s->scope > scope ){
        for( i= scope; i < s->scope - 1; i++) {
            sprintf(s_tmp, "\tmovl 8(%%esi), %%esi\n");
            add_machine_code( s_tmp );
        }
    }
*/
}

void loadAddr( char *R, char *a ) {
	char s_tmp[255];
    int f = stderr;
        
    if ( !isalpha(*a) ){
        sprintf(s_tmp, "\tmovl $%s, %%%s\n", a, R);
        add_machine_code( s_tmp );
    }else{
        SYMBOL *p = lookup(a);
		fprintf(stderr, "%s %d\n", p->name, p->level);
		exit(-1);
        /*
        if ( p->scope != scope ){
            // global
            getAR(p);
            
            if ( p->type == TYPE_TMP || p->type == TYPE_VAR ){
                sprintf(s_tmp, "\tleal %d(%%esi), %%%s\n", -4 - p->v.offset, R);
                add_machine_code( s_tmp );
            }
            
            if ( p->type == TYPE_PARAM && p->p.ptype == IN ){
                sprintf(s_tmp, "\tleal %d(%%esi), %%%s\n", 16 + (p->p.func->f.numpars-1)*4 - p->p.offset, R);
                add_machine_code( s_tmp );
            }
            
            if ( p->type == TYPE_PARAM && p->p.ptype == INOUT ){
                sprintf(s_tmp, "\tmovl %d(%%esi), %%%s\n", 16 + (p->p.func->f.numpars-1)*4 - p->p.offset, R);
                add_machine_code( s_tmp );
            }
            
        }else{
            
            if ( p->type == TYPE_TMP || p->type == TYPE_VAR ){
                sprintf(s_tmp, "\tleal %d(%%ebp), %%%s\n", -4 - p->v.offset, R);
                add_machine_code( s_tmp );
            }
            
            if ( p->type == TYPE_PARAM && p->p.ptype == IN ){
                sprintf(s_tmp, "\tleal %d(%%ebp), %%%s\n", 16 + (p->p.func->f.numpars-1)*4 - p->p.offset, R);
                add_machine_code( s_tmp );
            }
            
            if ( p->type == TYPE_PARAM && p->p.ptype == INOUT ){
                sprintf(s_tmp, "\tmovl %d(%%ebp), %%%s\n", 16 + (p->p.func->f.numpars-1)*4 - p->p.offset, R);
                add_machine_code( s_tmp );
            }
            
        }
		*/
    }

} 

void generate_final() {
	char s_tmp[255];
	
	while(gqp != NULL){
		QUAD q = gqp;
		//fprintf(stderr, "%s ", q->op);
		if (!strcmp(q->op,"begin_block")) {
			
			SYMBOL *tmp = lookup(q->x);
			assert(tmp != NULL);
			fprintf(stderr, "[B] %s %d\n", q->x, tmp->level);
			
			ncur = tmp->level+1;
		} else if (!strcmp(q->op,"end_block")) {
			fprintf(stderr, "\n");

		} else if ( strcmp( q->op, "jump") == 0){

            /* jump */
            sprintf(s_tmp, "\tjmp %s\n", label(q->z) );
            add_machine_code( s_tmp );

        } else if ( strcmp( q->op, "print") == 0){

            /* print */
            if( !isalpha(q->z[0]) ){
                sprintf(s_tmp, "\tmovl $%s, %%eax\n\tcall print\n", q->z );
                add_machine_code( s_tmp );
            }else{
                load("eax", q->z);
                sprintf(s_tmp, "\tcall print\n");
                add_machine_code( s_tmp );
            }

        } else if ( strcmp( q->op, ":=") == 0){

            /* assign */
            load("eax", q->x);
            store("eax", q->z);

        } else if ( strcmp( q->op, "+") == 0){

            /* add */
            load("eax", q->x);
            load("edx", q->y);
            sprintf(s_tmp, "\taddl %%edx, %%eax\n" );
            add_machine_code( s_tmp );
            store("eax", q->z);

        } else if ( strcmp( q->op, "-") == 0){

            /* sub */
            load("eax", q->x);
            load("edx", q->y);
            sprintf(s_tmp, "\tsubl %%edx, %%eax\n" );
            add_machine_code( s_tmp );
            store("eax", q->z);

        } else if ( strcmp( q->op, "*") == 0){

            /* mul */
            load("eax", q->x);
            load("ecx", q->y);
            sprintf(s_tmp, "\timul %%ecx\n" );
            add_machine_code( s_tmp );
            store("eax", q->z);

        } else if ( strcmp( q->op, "/") == 0){

            /* div */
            load("eax", q->x);
            load("ecx", q->y);
            sprintf(s_tmp, "\tidiv %%ecx\n" );
            add_machine_code( s_tmp );
            store("eax", q->z);

        } else if ( \
            strcmp( q->op, "=") == 0 ||\
            strcmp( q->op, "<") == 0 ||\
            strcmp( q->op, ">") == 0 ||\
            strcmp( q->op, "<=") == 0 ||\
            strcmp( q->op, ">=") == 0 || \
            strcmp( q->op, "<>") == 0 \
            ){

            /* cmp */

            load("eax", q->x);
            load("edx", q->y);

            sprintf(s_tmp, "\tcmp %%edx, %%eax\n" );
            add_machine_code( s_tmp );

            if ( strcmp( q->op, "=") == 0 ){
                sprintf(s_tmp, "\tje %s\n", label(q->z) );
                add_machine_code( s_tmp );
            }

            if ( strcmp( q->op, "<") == 0 ){
                sprintf(s_tmp, "\tjl %s\n", label(q->z) );
                add_machine_code( s_tmp );
            }

            if ( strcmp( q->op, ">") == 0 ){
                sprintf(s_tmp, "\tjg %s\n", label(q->z) );
                add_machine_code( s_tmp );
            }

            if ( strcmp( q->op, "<=") == 0 ){
                sprintf(s_tmp, "\tjle %s\n", label(q->z) );
                add_machine_code( s_tmp );
            }

            if ( strcmp( q->op, ">=") == 0 ){
                sprintf(s_tmp, "\tjge %s\n", label(q->z) );
                add_machine_code( s_tmp );
            }

            if ( strcmp( q->op, "<>") == 0 ){
                sprintf(s_tmp, "\tjne %s\n", label(q->z) );
                add_machine_code( s_tmp );
            }

        } else if ( strcmp( q->op, "call") == 0){
			SYMBOL *p = lookup( q->z );
            /* call */

            /* if procedure TODO */
/*
            if( p->func.type == PROC ){
                sprintf(s_tmp, "\tsubl $4, %%esp\n");
                add_machine_code( s_tmp );
            }

            if ( scope == 1 ){
                symbol *f = cross_link_head[ scope-1 ];
                for(;f->next_in_cross_link;f=f->next_in_cross_link){

                };                

                if ( strcmp(f->name, p->name) == 0 ){
                    updateAL(p, p);
                }else{
                    updateAL(f, p);
                }  
            }else{  
                symbol *f = cross_link_head[ scope ];              
                updateAL(f, p);
            }

            sprintf(s_tmp, "\tcall %s\n", q->z );
            add_machine_code( s_tmp );

            if ( p->f.params != NULL ){
                sprintf(s_tmp, "\taddl $%d, %%esp\n", p->f.params->p.offset + 8 );
                add_machine_code( s_tmp );
            }else{
                sprintf(s_tmp, "\taddl $%d, %%esp\n", 8 );
                add_machine_code( s_tmp );
            }    
*/
        } else if ( strcmp( q->op, "par") == 0){

            /* par */

            SYMBOL *p = lookup( q->z );

            if ( *(q->x) == 'V' ){
                load("eax", q->z);
                sprintf(s_tmp, "\tpushl %%eax\n");
                add_machine_code( s_tmp );      
            }else{
                loadAddr("esi", q->z);
                sprintf(s_tmp, "\tpushl %%esi\n"); 
                add_machine_code( s_tmp );               

                /* hack */
            }

        } else if ( strcmp( q->op, "retv") == 0){
            if( strcmp(q->x,"_") != 0 ){
                load("eax", q->x);
            }else{
                load("eax", "0");
            }

            /* 12(%ebp) is the return address. */

            sprintf(s_tmp, "\tmovl 12(%%ebp), %%esi\n");
            add_machine_code( s_tmp );
            sprintf(s_tmp, "\tmovl %%eax, (%%esi)\n");
            add_machine_code( s_tmp );

        }		
		gqp = gqp->prev;
	}
	
	
	print_machine_code();
	
}